#define USE_DEFAULT_CONFIGLOADING
//#define ALLOW_DEFAULT_EXECUTION

using System;
using System.Collections.Generic;
using System.Runtime.Remoting;

namespace SharpMud.Server.App.Console
{
    public class MainModule
    {
        public static Server.App.Hosting.ApplicationHost AppHost;

        private static readonly log4net.ILog log = log4net.LogManager.GetLogger("SharpMud.Server.App.Console");

        static MainModule()
        {
            log4net.Layout.ILayout defaultLayout = new log4net.Layout.PatternLayout
                (
                "%-5level| %logger{1}> %message%newline"
                );
            log4net.Appender.ConsoleAppender defaultAppender = new log4net.Appender.ConsoleAppender();
            defaultAppender.Layout = defaultLayout;
            
            log4net.Config.BasicConfigurator.Configure(defaultAppender);


            AppHost = new SharpMud.Server.App.Hosting.ApplicationHost();
        }

        private MainModule()
        {
        }

        [MTAThread]
        public static void Main(string[] args)
        {
            //TOOD: Add commandline support for specifying server app host configuration
            //TODO: Add commandline support for specifying server app host configuration location
            //TODO: Add commandline support to connect to a "remote" server app host, instead of instanciating a new one

            DisplayHeader();

#if ALLOW_DEFAULT_EXECUTION && USE_DEFAULT_CONFIGLOADING
			if(args.Length<=0)
			{
				AppHost.LoadConfiguration();

				AppHost.Initialize();
				AppHost.Log("");
				AppHost.AppExecHandle.Initialize();

				AppHost.AppExecHandle.Start();
				System.Threading.Thread.CurrentThread.IsBackground=true;
				AppHost.WaitForServerStop();

				AppHost.AppExecHandle.Stop();
				AppHost.AppExecHandle.Dispose();
			}
#endif
            if (
#if !(ALLOW_DEFAULT_EXECUTION && USE_DEFAULT_CONFIGLOADING)
(args.Length <= 0)
                ||
#endif
 (args[0] == "-?" || args[0] == "/?")
                )
            {

                DisplaySyntax();
                return;
            }

            if (args[0].ToLower() == "-connect" || args[0].ToLower() == "/connect")
            {
                Do_Connect(args);
                return;
            }
            if (args[0].ToLower() == "-execute" || args[0].ToLower() == "/execute")
            {
                Do_Execute(args);
                return;
            }
        }


        public static void Do_Execute(SharpMud.Server.App.Hosting.ApplicationHostConfiguration appHostConfig, bool activateIntegratedConfiguration)
        {
            if (activateIntegratedConfiguration)
            {
                throw new NotImplementedException("Integrated interactive configuration is not yet supported.");
            }
            else
            {
                AppHost.LoadConfiguration(appHostConfig);

                try
                {
                    AppHost.Initialize();
                }
                catch (SharpMud.Server.App.Hosting.HostAlreadyPresentException e)
                {
                    System.Console.WriteLine("SharpMud Server App Host is already running on the system.");
                    if (appHostConfig.RemotingPoint != null && appHostConfig.RemotingPoint != String.Empty)
                    {
                        System.Console.WriteLine("Attempting to connect at \"" + appHostConfig.RemotingPoint + "\"...");
                        Do_Connect(appHostConfig.RemotingPointUri, "start",false);
                        Do_Connect(appHostConfig.RemotingPointUri, "monitor", true);
                        return;
                    }
                    System.Console.WriteLine("Try using the remote server connectivity to monitor or control this already running instance.");
                    return;
                }
                AppHost.AppExecHandle.Initialize();

                AppHost.AppExecHandle.Start();
                System.Threading.Thread.CurrentThread.IsBackground = true;
                AppHost.WaitForServerStop();

                AppHost.AppExecHandle.Stop();
                AppHost.AppExecHandle.Dispose();
            }
        }

        public static void Do_Connect(System.Uri serverPost, string action, bool wait)
        {
            System.Runtime.Remoting.Channels.IChannel remotingChannel;
            IAppExecutionHandle appHandle;

            switch (serverPost.Scheme.ToLower().Trim())
            {
                case ("http"):
                    remotingChannel = new System.Runtime.Remoting.Channels.Http.HttpClientChannel();
                    break;
                case ("tcp"):
                    remotingChannel = new System.Runtime.Remoting.Channels.Tcp.TcpClientChannel();
                    break;
                case ("ipc"):
                    remotingChannel = new System.Runtime.Remoting.Channels.Ipc.IpcClientChannel();
                    break;
                default:
                    throw new System.InvalidOperationException("Unknown remoting protocol: " + serverPost.Scheme);
                    break;
            }

            System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel(remotingChannel);
            System.Runtime.Remoting.RemotingConfiguration.RegisterWellKnownClientType(typeof(IAppExecutionHandle), serverPost.ToString());
            appHandle = (IAppExecutionHandle)System.Runtime.Remoting.RemotingServices.Connect(typeof(IAppExecutionHandle), serverPost.ToString());
            if (appHandle == null)
            {
                System.Console.WriteLine();
                System.Console.WriteLine();
                System.Console.WriteLine("Unable to acquire remoting handle at " + serverPost.ToString());
                return;
            }

            //<program> -connect -post <remotingUri> <action> ["wait"]
            //   where <action> is:
            //      -start        (asks app host to start server)
            //      -stop         (asks app host to stop server)
            //      -pause        (asks app host to pause server)
            //      -resume       (asks app host to resume server
            //      -getStatus    
            //      -getInfo      
            //      -monitor 

            switch (action.ToLower().Trim())
            {
                case ("start"):
                    {
                        appHandle.Start();
                        break;
                    }
                case ("stop"):
                    {
                        appHandle.Stop();
                        break;
                    }
                case ("pause"):
                    {
                        appHandle.Pause();
                        break;
                    }
                case ("resume"):
                    {
                        appHandle.Resume();
                        break;
                    }
                case ("getStatus"):
                    {
                        AppStatus s = appHandle.Status;
                        System.Console.WriteLine();
                        System.Console.WriteLine();
                        System.Console.WriteLine("Server status at " + serverPost.ToString() + " is " + s.ToString());
                        System.Console.WriteLine();
                        return;
                        break;
                    }
                case ("getInfo"):
                    {
                        throw new NotImplementedException();
                        break;
                    }
                case ("monitor"):
                    {
                        AppStatus s = appHandle.Status;
                        System.Console.WriteLine();
                        System.Console.WriteLine();
                        System.Console.WriteLine("Server status at " + serverPost.ToString() + " is " + s.ToString());
                        System.Console.WriteLine();
                        System.Console.WriteLine("Monitoring server...");
                        System.Console.WriteLine();
                        s = appHandle.Status;
                        while(s!= AppStatus.Stopped && s!=AppStatus.Disposed)
                        {
                            System.Threading.Thread.Sleep(500);
                            s = appHandle.Status;
                        }
                        return;
                        break;
                    }
                default:
                    throw new InvalidOperationException("Unrecognized remote action: " + action);
                    break;
            }
        }

        public static void Do_Execute(string[] args)
        {
            if (args[0].ToLower() != "-execute" && args[0].ToLower() != "/execute")
            {
                throw new InvalidOperationException();
            }

            SharpMud.Server.App.Hosting.ApplicationHostConfiguration appHostConfig = new SharpMud.Server.App.Hosting.ApplicationHostConfiguration();
            int argPos = 1;

            if (args.Length < 2)
            {
                System.Console.WriteLine("Not enough parameters given.");
                System.Console.WriteLine("Either parameter \"-mudlib\" or \"-loadConfig\" expected.");
                DisplaySyntax();
                return;
            }
            string configFileName = String.Empty;
            if (args[1].ToLower() == "-loadconfig" || args[1].ToLower() == "/loadconfig")
            {
                if ((args.Length < 3)
                    || args[2].ToLower() == "-post" || args[2].ToLower() == "/post"
                    || args[2].ToLower() == "-interactiveconfigure" || args[2].ToLower() == "/interactiveconfigure"
                    || args[2].ToLower() == "-saveconfig" || args[2].ToLower() == "/saveconfig")
                {
#if USE_DEFAULT_CONFIGLOADING
                    configFileName = Application.GetSubfileIntelligently(AppHost.RootConfigFolder, "SharpMud.Server.App.config");
                    System.Console.WriteLine("Using default config file: ");
                    System.Console.WriteLine("\t" + configFileName);
                    argPos = 2;
#else
					System.Console.WriteLine("Not enough parameters given.");
					System.Console.WriteLine("A configuration file must be specified to be loaded.");
					DisplaySyntax();
					return;
#endif
                }
                else
                {
                    configFileName = args[2];
                    argPos = 3;
                }
                System.Console.Write("Loading server application host configuration...");
                System.IO.FileStream fs = new System.IO.FileStream(configFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read);
                System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(Server.App.Hosting.ApplicationHostConfiguration));
                appHostConfig = (Server.App.Hosting.ApplicationHostConfiguration)xs.Deserialize(fs);
                fs.Close();
                System.Console.Write("DONE.");
            }
            else if (args[1].ToLower() == "-mudlib" || args[1].ToLower() == "/mudlib")
            {
                if (args.Length < 4)
                {
                    System.Console.WriteLine("Not enough parameters given.");
                    System.Console.WriteLine("Parameters <assembly> and <type> expected after \"-mudlib\".");
                    DisplaySyntax();
                    return;
                }
                appHostConfig.ServerAppLocation = new SharpMud.DynamicTyping.AssembliedTypeLocation(args[2], args[3]);
                argPos = 4;
            }
            else
            {
                System.Console.WriteLine("Insufficient parameters gven.");
                System.Console.WriteLine("You must load a configuration with \"-loadConfig\", or specify one with \"-mudlib\".");
                DisplaySyntax();
                return;
            }
            //At this point, 'argPos' should index the next command line argument to process
            if (args.Length <= argPos)
            {
                //If 'argPos' indexes an argument that doesn't exist, then go on and execute the
                //server with what information we have.
                Do_Execute(appHostConfig, false);
                return;
            }
            if (args[argPos].ToLower() == "-post" || args[argPos].ToLower() == "/post")
            {
                if (args.Length < (argPos + 2))
                {
                    System.Console.WriteLine("Not enough parameters given.");
                    System.Console.WriteLine("A Server Listen-On Uri is expected after \"-post\".");
                    DisplaySyntax();
                    return;
                }
                appHostConfig.RemotingPoint = args[argPos + 1];
                argPos = argPos + 2;
            }
            if (args.Length <= argPos)
            {
                Do_Execute(appHostConfig, false);
            }
            if (args[argPos].ToLower() == "-saveconfig" || args[argPos].ToLower() == "/saveconfig")
            {
                if (
                    ((args.Length < (argPos + 1)
                    || (args[argPos + 1].ToLower() == "-interactiveconfigure" || args[argPos + 1].ToLower() == "/interactiveconfigure"))
                    )

                    && (configFileName == null || configFileName == String.Empty))
                {
                    System.Console.WriteLine("Not enough parameters given.");
                    System.Console.WriteLine("You must specify a filename to save the configuration to.");
                    DisplaySyntax();
                    return;
                }
                if (!
                    (
                    ((args.Length < (argPos + 1)
                    || (args[argPos + 1].ToLower() == "-interactiveconfigure" || args[argPos + 1].ToLower() == "/interactiveconfigure")))
                    )
                    )
                {
                    configFileName = args[argPos + 1];
                    argPos++;
                }
                System.Console.Write("Saving server application host configuration...");
                System.IO.FileStream fs = new System.IO.FileStream(configFileName, System.IO.FileMode.Create);
                System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(Server.App.Hosting.ApplicationHostConfiguration));
                xs.Serialize(fs, appHostConfig);
                fs.Close();
                System.Console.WriteLine("DONE.");
                argPos++;
            }
            if (args.Length <= argPos)
            {
                Do_Execute(appHostConfig, false);
            }
            bool interactiveMudlibConfigure = false;
            if (args[argPos].ToLower() == "-interactiveconfigure" || args[argPos].ToLower() == "/interactiveconfigure")
            {
                interactiveMudlibConfigure = true;
                argPos++;
            }
            if (!(args.Length <= argPos))
            {
                System.Console.WriteLine("Invalid or unrecognized extraneous parameters ignored.");
            }
            Do_Execute(appHostConfig, interactiveMudlibConfigure);
            return;
        }

        public static void Do_Connect(string[] args)
        {
            if (args[0].ToLower() == "-connect" || args[0].ToLower() == "/connect")
            {
                if (args.Length < 2)
                {
                    System.Console.WriteLine("Not enough parameters given.");
                    DisplaySyntax();
                    return;
                }

                if (args[1].ToLower() != "-post" && args[1].ToLower() != "/post")
                {
                    System.Console.WriteLine("Invalid or unexpected parameter given.");
                    DisplaySyntax();
                    return;
                }
                else
                {
                    if (args.Length < 3)
                    {
                        System.Console.WriteLine("Not enough parameters given.");
                        System.Console.WriteLine("Server remoting post point expected after \"-post\".");
                        DisplaySyntax();
                        return;
                    }
                    System.Uri uri = new Uri(args[2]);
                    if (uri.Scheme.ToLower() != "http" && uri.Scheme.ToLower() != "tcp" && uri.Scheme.ToLower() != "ipc")
                    {
                        System.Console.WriteLine("Invalid server remoting scheme.");
                        System.Console.WriteLine("Only HTTP, TCP, and IPC-based remoting is supported.");
                        DisplaySyntax();
                        return;
                    }
                    if (args.Length < 4)
                    {
                        System.Console.WriteLine("Not enough parameters given.");
                        System.Console.WriteLine("You must specify an action to perform on the remote server.");
                        DisplaySyntax();
                        return;
                    }
                    string action = args[3];
                    if (action.StartsWith("-") || action.StartsWith("/"))
                        action = action.Substring(1, action.Length - 1);
                    bool wait = false;
                    if (args.Length >= 5)
                    {
                        if (args[4].ToLower() == "/wait" || args[4].ToLower() == "-wait" || args[4].ToLower() == "wait")
                            wait = true;
                        else
                        {
                            System.Console.WriteLine("Unrecognized parameters ignored.");
                        }
                    }
                    if (args.Length > 5)
                        System.Console.WriteLine("Unrecognized parameters ignored.");
                    Do_Connect(uri, action, wait);
                    return;
                }
            }
            else
            {
                throw new InvalidOperationException();
            }
        }

        public static void DisplayHeader()
        {
            string consoleAppVersion = typeof(MainModule).Assembly.GetName().Version.ToString();
            string serverAppCoreVersion = typeof(Server.App.Application).Assembly.GetName().Version.ToString();

            System.Console.WriteLine("SharpMud.Server.App.Console, Version " + consoleAppVersion);
            System.Console.WriteLine("SharpMud.Server.App.Core,    Version " + serverAppCoreVersion);
            System.Console.WriteLine("(C)opyright 2004-2006 Bruce Markham. Licensed under the GNU LGPL.");
            System.Console.WriteLine();
        }

        public static void DisplaySyntax()
        {
            System.Console.WriteLine("Program Syntax:");
            System.Console.WriteLine();
            System.Console.WriteLine("   <program> -connect -post <remotingUri> <action> [\"wait\"]");
            System.Console.WriteLine("      where <action> is:");
            System.Console.WriteLine("         -start        (asks app host to start server)");
            System.Console.WriteLine("         -stop         (asks app host to stop server)");
            System.Console.WriteLine("         -pause        (asks app host to pause server)");
            System.Console.WriteLine("         -resume       (asks app host to resume server");
            System.Console.WriteLine("         -getStatus    ");
            System.Console.WriteLine("         -getInfo      ");
            System.Console.WriteLine("         -monitor      ");
            System.Console.WriteLine("   (If \"wait\" is used, then this program waits for the action to be completed.");
            System.Console.WriteLine();
            System.Console.WriteLine("   <program> -execute < -loadConfig <configFile> | -mudlib <assembly> <type> >");
            System.Console.WriteLine("      [ -post < <remotingUri> | <\"none\"> > ]");
            System.Console.WriteLine("      [ -interactiveConfigure ]");
            System.Console.WriteLine();
        }
    }
}
